package cn.gemiman.daoyi.module.netty.server;

import cn.gemiman.daoyi.framework.mq.redis.core.RedisMQTemplate;
import cn.gemiman.daoyi.module.netty.contant.ChannelAttrKey;
import cn.gemiman.daoyi.module.netty.contant.NettyRedisKey;
import cn.gemiman.daoyi.module.netty.mq.message.JhlIotED767RedisStreamMessage;
import cn.hutool.core.text.CharSequenceUtil;
import cn.hutool.core.util.StrUtil;
import cn.hutool.extra.spring.SpringUtil;
import cn.hutool.json.JSONUtil;
import io.netty.channel.Channel;
import io.netty.channel.ChannelHandlerContext;
import io.netty.channel.SimpleChannelInboundHandler;
import io.netty.handler.timeout.IdleState;
import io.netty.handler.timeout.IdleStateEvent;
import io.netty.util.AttributeKey;
import lombok.RequiredArgsConstructor;
import lombok.extern.slf4j.Slf4j;
import org.springframework.data.redis.core.RedisTemplate;

import java.net.InetAddress;
import java.net.InetSocketAddress;
import java.util.List;

/**
 * WebSocket 长连接下 文本帧的处理器
 * 实现浏览器发送文本回写
 * 浏览器连接状态监控
 */
@Slf4j
@RequiredArgsConstructor
public class TcpChannelHandler extends SimpleChannelInboundHandler<Object> {

    private final RedisMQTemplate redisMQTemplate;

    /**
     * 读取到消息后进行处理
     *
     * @param ctx      channel上下文
     * @param sendInfo 发送消息
     */
    @Override
    protected void channelRead0(ChannelHandlerContext ctx, Object sendInfo) {
        if (null == sendInfo) {
            return;
        }
        String sendInfoString = sendInfo.toString();
        if (CharSequenceUtil.startWith(sendInfoString, "[")
                && CharSequenceUtil.endWith(sendInfoString, "]")
                && CharSequenceUtil.split(sendInfoString, "*").size() == 4) {
            // 精华隆 IOT 通讯协议
            redisMQTemplate.send(new JhlIotED767RedisStreamMessage()
                    .setBody(sendInfoString)
                    .setClientIp(getClientAddress(ctx))
            );
        }
        if (JSONUtil.isTypeJSONObject(sendInfoString)) {
            log.info(sendInfoString);
            List<String> sendInfoList = StrUtil.split(sendInfoString, "}{")
                    .stream()
                    .map(str -> {
                        if (!str.startsWith("{")) {
                            str = "{" + str;
                        }
                        if (!str.endsWith("}")) {
                            str = str + "}";
                        }
                        return str;
                    })
                    .toList();
            sendInfoList.parallelStream().forEach(str -> {
                JhlIotED767RedisStreamMessage msg = JSONUtil.toBean(str, JhlIotED767RedisStreamMessage.class);
                if (CharSequenceUtil.isAllNotBlank(msg.getChannelId(), msg.getBody(), msg.getClientIp())) {
                    // 精华隆 IOT 通讯协议[转发]
                    if (CharSequenceUtil.startWith(msg.getBody(), "[")
                            && CharSequenceUtil.endWith(msg.getBody(), "]")
                            && CharSequenceUtil.split(msg.getBody(), "*").size() == 4) {
                        redisMQTemplate.send(msg);
                    }
                }
            });
        } else {
            log.info(sendInfoString);
            // 创建处理器进行处理
//        AbstractMessageProcessor processor = ProcessorFactory.createProcessor(IMCmdType.fromCode(sendInfo.getCmd()));
//        processor.process(ctx, processor.transForm(sendInfo.getData()));
            ctx.channel().writeAndFlush(sendInfo);
        }
    }

    /**
     * 出现异常的处理 打印报错日志
     *
     * @param ctx   channel上下文
     * @param cause 异常信息
     */
    @Override
    public void exceptionCaught(ChannelHandlerContext ctx, Throwable cause) {
        log.error(cause.getMessage());
        //关闭上下文
        //ctx.close();
    }

    /**
     * 监控浏览器上线
     *
     * @param ctx channel上下文
     */
    @Override
    public void handlerAdded(ChannelHandlerContext ctx) {
        log.info("{} - {}连接", getClientAddress(ctx), ctx.channel().id().asLongText());
    }

    @Override
    public void handlerRemoved(ChannelHandlerContext ctx) {
        AttributeKey<Long> userIdAttr = AttributeKey.valueOf(ChannelAttrKey.USER_ID);
        Long userId = ctx.channel().attr(userIdAttr).get();
        AttributeKey<Integer> terminalAttr = AttributeKey.valueOf(ChannelAttrKey.TERMINAL_TYPE);
        Integer terminal = ctx.channel().attr(terminalAttr).get();
        ChannelHandlerContext context = UserChannelCtxMap.getChannelCtx(userId, terminal);
        // 判断一下，避免异地登录导致的误删
        if (context != null && ctx.channel().id().equals(context.channel().id())) {
            // 移除channel
            UserChannelCtxMap.removeChannelCtx(userId, terminal);
            // 用户下线
            RedisTemplate<String, Object> redisTemplate = SpringUtil.getBean("redisTemplate");
            String key = String.join(":", NettyRedisKey.IM_USER_SERVER_ID, userId.toString(), terminal.toString());
            redisTemplate.delete(key);
            log.info("断开连接,userId:{},终端类型:{}", userId, terminal);
        }
    }

    @Override
    public void userEventTriggered(ChannelHandlerContext ctx, Object evt) throws Exception {
        if (evt instanceof IdleStateEvent) {
            IdleState state = ((IdleStateEvent) evt).state();
            if (state == IdleState.READER_IDLE) {
                // 在规定时间内没有收到客户端的上行数据, 主动断开连接
                AttributeKey<Long> attr = AttributeKey.valueOf("USER_ID");
                Long userId = ctx.channel().attr(attr).get();
                AttributeKey<Integer> terminalAttr = AttributeKey.valueOf(ChannelAttrKey.TERMINAL_TYPE);
                Integer terminal = ctx.channel().attr(terminalAttr).get();
                // log.info("心跳超时，即将断开连接,用户id:{},终端类型:{} ", userId, terminal);
                // ctx.channel().close();
            }
        } else {
            super.userEventTriggered(ctx, evt);
        }
    }

    private String getClientAddress(ChannelHandlerContext ctx) {
        Channel channel = ctx.channel();
        InetSocketAddress ipSocket = (InetSocketAddress) channel.remoteAddress();
        InetAddress address = ipSocket.getAddress();
        String clientIp = address.getHostAddress();
        int port = ipSocket.getPort();
        return clientIp + ":" + port;
    }
}
